#ifndef CVAR_H
#define	CVAR_H
#include <string>
#include <map>
#include <iostream>
#include <fstream>
#include <sstream>
#include <typeinfo>
#include "..\utils\GameMath.h"

template <class T>
std::string CVarValueString( T *t )
{
    std::ostringstream oss;
    oss << *t;
    return oss.str();
}

template <class T>
std::string CVarTypeString( T *t )
{
    std::ostringstream oss;
    oss << typeid( *t ).name();
    return oss.str();
}

template <class T>
void StringToCVarValue( T *t, const std::string &sValue )
{
	std::istringstream iss( sValue );
	iss >> *t;
}

template<class T>
class CVar {

public:
    CVar(const std::string& name,T value,
            std::ostream& (*pSerialisationFuncPtr)( std::ostream &, T ) = NULL,
            std::istream& (*pDeserialisationFuncPtr)( std::istream &, T ) = NULL ) {
        this->name = name;
        data = new T;
        *data = value;
        m_pTypeStringFuncPtr = CVarTypeString;
        m_pValueStringFuncPtr = CVarValueString;
        m_pSerialisationFuncPtr   = pSerialisationFuncPtr;
        m_pDeserialisationFuncPtr = pDeserialisationFuncPtr;
		 m_pSetValueFuncPtr = StringToCVarValue;
    }
    ~CVar() {
        if ( data != NULL ) {
            delete data;
        }
    }
    // attributes
    std::string name;
    T* data;
    std::string type() {
        return (*m_pTypeStringFuncPtr)(data);
    }
    std::string getAsString() {
        if( m_pSerialisationFuncPtr != NULL ) {
            std::stringstream sStream( "" );
            m_pSerialisationFuncPtr( sStream, *data );
            return sStream.str();
        }
        else {
            return (*m_pValueStringFuncPtr)(data );
        }
    }
    void fromString(const std::string& value) {		
		if( m_pDeserialisationFuncPtr != NULL ) {
			std::stringstream sStream( value );
			m_pDeserialisationFuncPtr( sStream, *data );
		}
		else {
			if( m_pSetValueFuncPtr != NULL ) {
				(*m_pSetValueFuncPtr)( data, value );
			}
		}		
    }
private:
    std::string (*m_pTypeStringFuncPtr)( T *t );
    std::string (*m_pValueStringFuncPtr)( T *t );
    std::ostream& (*m_pSerialisationFuncPtr)( std::ostream &, T );
    std::istream& (*m_pDeserialisationFuncPtr)( std::istream &, T ) ;
	void (*m_pSetValueFuncPtr)( T *t, const std::string & ); 
};

class CVarNode {

public:
    CVarNode(const std::string& name,void* data) {
        this->name = name;
        nodeData = data;
    }
    std::string name;
    void* nodeData;
};

std::ostream &operator<<(std::ostream &stream, CVarNode &node );
std::ostream &operator<<(std::ostream &stream, ds::Vec3 &v );

class CVarContainer {

typedef std::map<std::string,CVarNode*> Nodes;
public:
    template<class T> T& create(const std::string& name,T value) {
        if( m_Nodes.find(name) == m_Nodes.end() ) {
            CVar<T> *pCVar = new CVar<T>(name,value);
            m_Nodes[name] = new CVarNode(name,(void*)pCVar);
            return *(pCVar->data);
        }
        else {
            return get<T>(name);
        }
    }
    template<class T> T& get(const std::string& name) {
        CVarNode* tmp = m_Nodes[name];
        return *((CVar<T>*)tmp->nodeData)->data;
    }
    template<class T> void set(const std::string& name,T value) {
        CVarNode* tmp = m_Nodes[name];
        *(((CVar<T>*)tmp->nodeData)->data) = value;
    }
    void save(const std::string& fileName) {
        std::ofstream sOut( fileName.c_str() );
        if( sOut.is_open() ) {
            Nodes::iterator it;
            for ( it = m_Nodes.begin();it != m_Nodes.end();++it ) {
                sOut << *it->second;
            }
            sOut.close();
        }
    }    
	void load( const std::string& fileName) {
		std::ifstream sIn( fileName.c_str() );
		if( sIn.is_open() ) {			
			//sIn >> *g_pCVarTrie;
			sIn.close();
		}
	}  
	CVarNode* find(const std::string& name) {
		return m_Nodes[name];
	}
	void setFromString(const std::string& name,const std::string& value) {
		CVarNode* tmp = m_Nodes[name];
		((CVar<int>*)tmp->nodeData)->fromString(value);
	}
private:
    Nodes m_Nodes;
};



extern CVarContainer* gCV;
#endif	/* CVAR_H */

